﻿#include <list>
#include <condition_variable>
#include "Timer.h"

using namespace ThreadSafe;

struct Timer::TimerData
{
	Timer* p;
	Timer::TimerEvent * event;
};

struct Timer::TimerEvent {
	std::chrono::system_clock::time_point point;
	std::function<void()> handle;
	
};

class Timer::TimerEventManager
{
public:
	TimerEventManager() {
		isStop = false;
		loop_thread.reset(new std::thread([=]() {Loop(); }));
		MaxId = 1;
	}
	~TimerEventManager() {
		isStop = true;
		loop_thread->join();
		loop_thread.reset();
	}

	TimerId RegisterTimer(Timer* timer) {
		std::unique_lock<std::mutex> lock(m_mute);

		TimerId id = MaxId;
		MaxId++;

		TimerData data;
		data.p = timer;
		data.event = NULL;
		timers[id] = data;

		return id;

	}

	void UnRegisterTimer(TimerId id) {
		std::unique_lock<std::mutex> lock(m_mute);
		if (timers.count(id)) {
			auto & data = timers.at(id);
			DoCancelEvent(data.event);
			timers.erase(id);
		}
	}

	void PostEvent(TimerId id,std::chrono::system_clock::time_point time) {
		std::unique_lock<std::mutex> lock(m_mute);
		if (timers.count(id)) {
			auto & data = timers.at(id);
			DoCancelEvent(data.event);
			TimerEvent* event = new TimerEvent();
			event->handle = [=]() {
				std::unique_lock<std::mutex> lock(m_mute);
				if (timers.count(id)) {
					auto & data = timers.at(id);
					auto timer = data.p;
					EventLoop::PostEvent(timer,new ThreadSafe::TimerEvent());
				}
			};
			event->point = time;
			data.event = event;

			DoPostEvent(event);
		}
	}

	void CancelEvent(TimerId id) {
		std::unique_lock<std::mutex> lock(m_mute);
		if (timers.count(id)) {
			auto & data = timers.at(id);
			DoCancelEvent(data.event);
			data.event = NULL;
		}
	}

	void PostEvent(TimerEvent* event) {
		std::unique_lock<std::mutex> lock(m_mute);
		DoPostEvent(event);
	}

	

	void CancelEvent(TimerEvent* event) {
		std::unique_lock<std::mutex> lock(m_mute);
		DoCancelEvent(event);
	}

	static TimerEventManager* Instance() {
		static TimerEventManager Instance;
		return &Instance;
	}

private:
	void DoPostEvent(TimerEvent* event) {
		auto it = event_queue.begin();
		for (; it != event_queue.end(); it++) {
			if ((*it)->point > event->point) {
				break;
			}
		}
		event_queue.insert(it, event);
		m_cond.notify_one();
	}

	void DoCancelEvent(TimerEvent* event) {
		if (!event) return;
		auto it = event_queue.begin();
		for (; it != event_queue.end(); it++) {
			if ((*it) == event) {
				auto p = *it;
				event_queue.erase(it);
				delete p;
				return;
			}
		}
	}

	void Loop() {

		while (true)
		{
			if (isStop) return;

			TimerEvent *event = NULL;

			{
				std::unique_lock<std::mutex> lock(m_mute);
				while (true)
				{
					if (isStop) return;
					if (event_queue.empty()) {
						m_cond.wait(lock);
						continue;
					}

					auto & f = event_queue.front();
					auto now = std::chrono::system_clock::now();
					auto dis = f->point - now;
					if (dis > std::chrono::milliseconds(0))
					{
						m_cond.wait_for(lock, dis);
						continue;
					}

					event = event_queue.front();
					event_queue.pop_front();
					break;
				}
			}

			event->handle();
			delete event;
		}
	}

private:
	bool isStop;
	std::list<TimerEvent*> event_queue;
	std::shared_ptr<std::thread> loop_thread;
	std::mutex m_mute;
	std::condition_variable m_cond;
	std::unordered_map<TimerId, TimerData> timers;
	TimerId MaxId;
};



Timer::Timer()
{
	currentThread = std::this_thread::get_id();
	interval = std::chrono::milliseconds(0);
	m_bisActive = false;
	isPosted = false;
	id = TimerEventManager::Instance()->RegisterTimer(this);
}


Timer::~Timer()
{
	Stop();
	TimerEventManager::Instance()->UnRegisterTimer(id);
}

void Timer::SetInterval(std::chrono::milliseconds interval)
{
	this->interval = interval;
	if (m_bisActive) {
		PostEvent(std::chrono::system_clock::now());
	}
}

std::chrono::milliseconds Timer::GetInterval()
{
	return interval;
}

void Timer::Start()
{
	if (m_bisActive) {
		return;
	}

	PostEvent(std::chrono::system_clock::now());

	m_bisActive = true;
}

void Timer::Start(std::chrono::milliseconds interval)
{
	SetInterval(interval);
	Start();
}

void Timer::Stop()
{
	if (!m_bisActive) {
		return;
	}

	TimerEventManager::Instance()->CancelEvent(id);

	m_bisActive = false;
}

bool Timer::isActive() const
{
	return m_bisActive;
}

void Timer::SingleShot(std::chrono::milliseconds time, std::function<void()> fun, ThreadSafe::ThreadSafeBase* context)
{
	TimerEvent * event = new TimerEvent();
	event->handle = [=]() {
		EventLoop::PostEvent(context, new ThreadSafe::InvokeFunctionEvent(fun));
	};
	event->point = std::chrono::system_clock::now() + time;
	TimerEventManager::Instance()->PostEvent(event);
}

void Timer::SingleShot(std::chrono::system_clock::time_point timepoint, std::function<void()> fun, ThreadSafe::ThreadSafeBase* context)
{
	TimerEvent * event = new TimerEvent();
	ThreadSafe::ObjectPointer<ThreadSafe::ThreadSafeBase> pointer(context);
	event->handle = [=]() {
		auto i = pointer.get();
		if (i) {
			EventLoop::PostEvent(context, new ThreadSafe::InvokeFunctionEvent(fun));
		}
	};
	event->point = timepoint;
	TimerEventManager::Instance()->PostEvent(event);
}

void Timer::HandleTimerEvent(ThreadSafe::TimerEvent* event)
{
	auto now = std::chrono::system_clock::now();
	isPosted = false;
	if (!m_bisActive) {
		return;
	}

	TimeOut();

	if (m_bisActive && !isPosted) {
		PostEvent(now);
	}
}

void Timer::HandleEvent(ThreadSafe::Event * event)
{
	if (event->GetType()) {
		HandleTimerEvent(static_cast<ThreadSafe::TimerEvent*>(event));
	}

	ThreadSafe::ThreadSafeBase::HandleEvent(event);
}

void Timer::PostEvent(std::chrono::system_clock::time_point startTime)
{

	std::chrono::system_clock::time_point next = startTime;
	do
	{
		next += interval;
	} while (next < std::chrono::system_clock::now());
	
	TimerEventManager::Instance()->PostEvent(id, next);
	isPosted = true;
}
